package com.example.util;

import java.security.SecureRandom;
import java.util.Random;

/**
 * ID生成器 - 基于ULID算法
 * ULID (Universally Unique Lexicographically Sortable Identifier)
 * 
 * 特点：
 * 1. 按时间排序
 * 2. URL安全
 * 3. 长度26个字符
 * 4. 大小写不敏感的Base32编码
 * 
 * @author Means
 * @since 2025-10-27
 */
public class IdGenerator {
    
    /**
     * 私有构造函数，防止实例化
     */
    private IdGenerator() {
        throw new UnsupportedOperationException("Utility class");
    }
    
    private static final char[] ENCODING_CHARS = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
        'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z'
    };
    
    private static final Random RANDOM = new SecureRandom();
    
    /**
     * 生成ULID
     * 格式：TTTTTTTTTTRRRRRRRRRRRRRRRR
     * - T: 时间戳部分（10个字符）
     * - R: 随机部分（16个字符）
     * 
     * @return ULID字符串
     */
    public static String generate() {
        long timestamp = System.currentTimeMillis();
        return generate(timestamp);
    }
    
    /**
     * 根据指定时间戳生成ULID
     * 
     * @param timestamp 时间戳（毫秒）
     * @return ULID字符串
     */
    public static String generate(long timestamp) {
        char[] chars = new char[26];
        
        // 时间戳部分（10个字符）
        encodeTime(timestamp, chars, 0);
        
        // 随机部分（16个字符）
        encodeRandom(chars, 10);
        
        return new String(chars);
    }
    
    /**
     * 编码时间戳
     * 
     * @param timestamp 时间戳
     * @param chars 字符数组
     * @param offset 偏移量
     */
    private static void encodeTime(long timestamp, char[] chars, int offset) {
        chars[offset + 0] = ENCODING_CHARS[(int) ((timestamp >>> 45) & 0x1F)];
        chars[offset + 1] = ENCODING_CHARS[(int) ((timestamp >>> 40) & 0x1F)];
        chars[offset + 2] = ENCODING_CHARS[(int) ((timestamp >>> 35) & 0x1F)];
        chars[offset + 3] = ENCODING_CHARS[(int) ((timestamp >>> 30) & 0x1F)];
        chars[offset + 4] = ENCODING_CHARS[(int) ((timestamp >>> 25) & 0x1F)];
        chars[offset + 5] = ENCODING_CHARS[(int) ((timestamp >>> 20) & 0x1F)];
        chars[offset + 6] = ENCODING_CHARS[(int) ((timestamp >>> 15) & 0x1F)];
        chars[offset + 7] = ENCODING_CHARS[(int) ((timestamp >>> 10) & 0x1F)];
        chars[offset + 8] = ENCODING_CHARS[(int) ((timestamp >>> 5) & 0x1F)];
        chars[offset + 9] = ENCODING_CHARS[(int) (timestamp & 0x1F)];
    }
    
    /**
     * 编码随机部分
     * 
     * @param chars 字符数组
     * @param offset 偏移量
     */
    private static void encodeRandom(char[] chars, int offset) {
        byte[] randomBytes = new byte[10];
        RANDOM.nextBytes(randomBytes);
        
        chars[offset + 0] = ENCODING_CHARS[(randomBytes[0] & 0xFF) >>> 3];
        chars[offset + 1] = ENCODING_CHARS[((randomBytes[0] << 2) | (randomBytes[1] & 0xFF) >>> 6) & 0x1F];
        chars[offset + 2] = ENCODING_CHARS[(randomBytes[1] & 0xFF) >>> 1 & 0x1F];
        chars[offset + 3] = ENCODING_CHARS[((randomBytes[1] << 4) | (randomBytes[2] & 0xFF) >>> 4) & 0x1F];
        chars[offset + 4] = ENCODING_CHARS[((randomBytes[2] << 1) | (randomBytes[3] & 0xFF) >>> 7) & 0x1F];
        chars[offset + 5] = ENCODING_CHARS[(randomBytes[3] & 0xFF) >>> 2 & 0x1F];
        chars[offset + 6] = ENCODING_CHARS[((randomBytes[3] << 3) | (randomBytes[4] & 0xFF) >>> 5) & 0x1F];
        chars[offset + 7] = ENCODING_CHARS[randomBytes[4] & 0x1F];
        chars[offset + 8] = ENCODING_CHARS[(randomBytes[5] & 0xFF) >>> 3];
        chars[offset + 9] = ENCODING_CHARS[((randomBytes[5] << 2) | (randomBytes[6] & 0xFF) >>> 6) & 0x1F];
        chars[offset + 10] = ENCODING_CHARS[(randomBytes[6] & 0xFF) >>> 1 & 0x1F];
        chars[offset + 11] = ENCODING_CHARS[((randomBytes[6] << 4) | (randomBytes[7] & 0xFF) >>> 4) & 0x1F];
        chars[offset + 12] = ENCODING_CHARS[((randomBytes[7] << 1) | (randomBytes[8] & 0xFF) >>> 7) & 0x1F];
        chars[offset + 13] = ENCODING_CHARS[(randomBytes[8] & 0xFF) >>> 2 & 0x1F];
        chars[offset + 14] = ENCODING_CHARS[((randomBytes[8] << 3) | (randomBytes[9] & 0xFF) >>> 5) & 0x1F];
        chars[offset + 15] = ENCODING_CHARS[randomBytes[9] & 0x1F];
    }
    
    /**
     * 从ULID中提取时间戳
     * 
     * @param ulid ULID字符串
     * @return 时间戳（毫秒）
     */
    public static long extractTimestamp(String ulid) {
        if (ulid == null || ulid.length() != 26) {
            throw new IllegalArgumentException("Invalid ULID format");
        }
        
        long timestamp = 0;
        for (int i = 0; i < 10; i++) {
            int value = decodeChar(ulid.charAt(i));
            timestamp = (timestamp << 5) | value;
        }
        return timestamp;
    }
    
    /**
     * 解码字符
     * 
     * @param c 字符
     * @return 解码值
     */
    private static int decodeChar(char c) {
        if (c >= '0' && c <= '9') {
            return c - '0';
        }
        if (c >= 'A' && c <= 'Z') {
            char upperC = c;
            if (upperC == 'I' || upperC == 'L' || upperC == 'O' || upperC == 'U') {
                throw new IllegalArgumentException("Invalid ULID character: " + c);
            }
            // 映射到Base32
            if (upperC <= 'H') return upperC - 'A' + 10;
            if (upperC <= 'K') return upperC - 'A' + 10 - 1;
            if (upperC <= 'N') return upperC - 'A' + 10 - 1;
            if (upperC <= 'T') return upperC - 'A' + 10 - 2;
            if (upperC <= 'Z') return upperC - 'A' + 10 - 3;
        }
        if (c >= 'a' && c <= 'z') {
            return decodeChar((char) (c - 32)); // 转大写
        }
        throw new IllegalArgumentException("Invalid ULID character: " + c);
    }
}

